def fun1(fun0):
    def fun2(x, y):        #就是将fun2函数里的东西堆到fun0里边，从而使fun0具有更多的特性
        print(x + y)
        return fun0(x,y)   #堆积完成后将fun0原封不动的返回出去
    return fun2




@fun1
def fun0(x,y):
    print(x - y)
fun0(1,3)


##装饰器就是不改变原有函数代码，对其进行装饰，使其具有新的的方法

01  什么是装饰器？装饰器可以让一个Python函数拥有原本没有的功能，
也就是你可以通过装饰器，让一个平淡无奇的函数变的强大，变的漂亮。
举几个现实中的例子

1、你一个男的程序员，穿上女装，戴上假发，你就有了女人的外表（穿女装、戴假发的过程就是新的特效，你拥有了女人的外表，你原来的小jj还在，没有消失）
2、你新买的毛坯房，装修，买家具后变好看了（装修、家具就是新的特效）
3、孙悟空被放进炼丹炉装饰了一下，出来后，学会了火眼金睛，以前的本领都还在

02  为什么Python要引入装饰器？因为引入装饰器会便于开发，便于代码复用，可以把烂泥扶上墙，装饰器可以让你一秒变女人且可以保住小JJ，当你某天后悔想重新变回男人，只要脱掉女装和假发即可（如果你变女人的时候，给小JJ做了手术（直接修改函数体的内容），想再变回男人可就痛苦了哦）03 装饰器有利于解决哪些问题？例子1：扩展功能比如你写了一段代码，当你执行 孙悟空() 就打印出它目前的技能# python3支持用中文做函数名，


def 孙悟空():
  print('吃桃子')
孙悟空()

# 输出:
# 吃桃子
现在你希望孙悟空这个函数 打印出 ’有火眼金睛了’，该怎么做呢？是的，你可以直接在函数里加一段 print('有火眼金睛了')
但是这样会破坏原来的代码，如果你的代码量很多很多的话，修改起来则是灾难，不过别担心，你还可以用装饰器来装饰他，
让他在原本基础上，扩展出新的功能
代码如下
def 炼丹炉(func): # func就是‘孙悟空’这个函数
    def 变身(*args, **kwargs): #*args, **kwargs就是‘孙悟空’的参数列表，这里的‘孙悟空’函数没有传参数，我们写上也不影响，建议都写上  
      print('有火眼金睛了') # 加特效，增加新功能，比如孙悟空的进了炼丹炉后，有了火眼金睛技能  
      return func(*args, **kwargs) #保留原来的功能，原来孙悟空的技能，如吃桃子
return 变身 # 炼丹成功，更强大的，有了火眼金睛技能的孙悟空出世

@炼丹炉
def 孙悟空():
  print('吃桃子')

孙悟空()
# 输出:
# 有火眼金睛了
# 吃桃子例子2：扩展权限认证比如你的代码，默认打开就播放动画片，代码如下def play():
  print('开始播放动画片 《喜洋洋和灰太狼》')

play()
# 输出
# 开始播放动画片 《喜洋洋和灰太狼》但是突然某天，你突然希望只允许1岁到10才可以看这个动画片，不希望程序员大叔看这个动画片怎么办？是的，你可以修改这个代码，加上年龄限制，但如果我们用装饰器的话，就更简单了，就可以不用破坏原来的代码，而且方便扩展到其他函数上userAge = 40

def canYou(func):
  def decorator(*args, **kwargs):
      if userAge > 1 and userAge < 10:
          return func(*args, **kwargs)
      print('你的年龄不符合要求，不能看')
  return decorator

@canYou
def play():
  print('开始播放动画片 《喜洋洋和灰太狼》')

play()
# 输出
# 你的年龄不符合要求，不能看
# 你可以修改上面的 userAge 为9 试试你看，是不是很简单，实际情况中，很多时候，你需要对一段代码加上权限认证，加上各种功能；但是又不想，或者不方便破坏原有代码，则可以用装饰器去扩展它04 装饰器背后的实现原理是什么？原理 代码逆推后如下def 炼丹炉(func): 
  def 变身(*args, **kwargs):  
      print('有火眼金睛了') 
      return func(*args, **kwargs) 
  return 变身 

def 孙悟空():  
  print('吃桃子')
新_孙悟空 = 炼丹炉(孙悟空) #放入原料，原来的弱小的孙悟空，生成炼丹方案给 新_孙悟空 ，这里也可以把炼丹方案给 原来的‘孙悟空’，为了方便理解，给了新的孙悟空 

新_孙悟空() # 执行炼丹程序，新的孙悟空出世然后这段代码，写起来有点麻烦，Python官方出了一个快捷代码，也就是语法糖，用了语法糖就变成了下面这样def 炼丹炉(func): 
  def 变身(*args, **kwargs):  
      print('有火眼金睛了') 
      return func(*args, **kwargs) 
  return 变身 

@炼丹炉  # 把下面的 ‘孙悟空’ 塞进炼丹炉，并把新的孙悟空复制给下面的函数
def 孙悟空():  
  print('吃桃子')

孙悟空() # 执行炼丹程序，新的孙悟空出世可以一次性在一个函数上用多个装饰器吗？当然可以，下面我们给孙悟空，弄个金箍棒，让他学会72变，学会飞def 炼丹炉(func):
  def 变身(*args, **kwargs):
      print('有火眼金睛了')
      return func(*args, **kwargs)
  return 变身

def 龙宫走一趟(func):
  def 你好(*args, **kwargs):
      print('有金箍棒了')
      return func(*args, **kwargs)
  return 你好

def 拜师学艺(func):
  def 师傅(*args, **kwargs):
      print('学会飞、72变了')
      return func(*args, **kwargs)
  return 师傅

@拜师学艺
@龙宫走一趟
@炼丹炉  
def 孙悟空():
  print('吃桃子')

孙悟空()
# 输出
# 学会飞、72变了
# 有金箍棒了
# 有火眼金睛了
# 吃桃子上面代码的等效于 拜师学艺(龙宫走一趟(炼丹炉(孙悟空)))代码的执行顺序是 先从内到外先执行 炼丹炉，然后是龙宫走一趟，最后是拜师学艺
